package com.zmc.member.application.impl;

import com.zmc.common.annotation.PreventRepeatSubmit;
import com.zmc.common.constant.CacheConstants;
import com.zmc.common.domain.vo.member.MemberInfo;
import com.zmc.common.domain.vo.third.SmsVo;
import com.zmc.common.enums.SerialCodeEnum;
import com.zmc.common.exception.base.BusinessException;
import com.zmc.common.exception.base.WebDisplayException;
import com.zmc.common.utils.DateUtils;
import com.zmc.common.utils.MD5Utils;
import com.zmc.common.utils.RegularValidUtils;
import com.zmc.common.utils.uuid.SnowflakeIdUtils;
import com.zmc.common.utils.StringUtils;
import com.zmc.common.utils.redis.RedisCache;
import com.zmc.common.utils.token.AuthTokenService;
import com.zmc.common.utils.uuid.IdUtils;
import com.zmc.member.application.MemberApplicationService;
import com.zmc.member.infrastructure.bean.vo.MemberCodeVo;
import com.zmc.member.infrastructure.bean.vo.MemberPhoneVo;
import com.zmc.member.infrastructure.bean.vo.MemberPwdVo;
import com.zmc.member.infrastructure.bean.vo.MemberVo;
import com.zmc.member.infrastructure.constant.MemberConstants;
import com.zmc.member.infrastructure.entity.MemberAuthEmailEntity;
import com.zmc.member.infrastructure.entity.MemberAuthUsernameEntity;
import com.zmc.member.infrastructure.entity.MemberEntity;
import com.zmc.member.infrastructure.bean.query.LoginQuery;
import com.zmc.member.infrastructure.service.MemberAuthEmailService;
import com.zmc.member.infrastructure.service.MemberAuthUsernameService;
import com.zmc.member.infrastructure.service.MemberService;
import com.zmc.member.infrastructure.enums.MemberPhoneEnum;
import com.zmc.member.exception.*;
import com.zmc.member.feign.ThirdFeignService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Author：zmc
 * @Date：2024/10/30 17:21
 */
@Component
public class MemberApplicationServiceImpl implements MemberApplicationService {

    public static Logger log = LoggerFactory.getLogger(MemberApplicationServiceImpl.class);

    @Autowired
    private MemberService memberService;

    @Autowired
    private MemberAuthUsernameService memberAuthUsernameService;

    @Autowired
    private MemberAuthEmailService memberAuthEmailService;

    @Autowired
    private ThirdFeignService thirdFeignService;

    @Autowired
    private AuthTokenService authTokenService;

    @Autowired
    private RedisCache redisCache;

    @Override
    @Transactional
    public MemberInfo login(LoginQuery query) {
        //登录前置校验
        MemberEntity memberEntity = query.isType() ? validatePhone(query) : validateUser(query);
        log.info("登录成功, userId:{}", memberEntity.getId());
        return constructMemberInfo(memberEntity);
    }

    @Override
    @Transactional
    public void updateMemberBaseInfo(MemberVo vo) {
        String memberId = authTokenService.getMemberInfo().getMemberId();
        MemberEntity memberEntity = memberService.getByMemberId(memberId);
        //验证邮箱唯一性
        String email = vo.getEmail();
        MemberAuthEmailEntity emailEntity = memberAuthEmailService.queryByEmail(email);
        if (emailEntity != null && !emailEntity.getMemberId().equals(memberId)) {
            throw new MemberEmailRepeatException();
        }
        //验证账号唯一性
        String username = vo.getUsername();
        MemberAuthUsernameEntity usernameEntity = memberAuthUsernameService.queryByUsername(username);
        if (usernameEntity != null && !memberId.equals(usernameEntity.getMemberId())) {
            throw new MemberUsernameRepeatException();
        }
        if (emailEntity != null && !memberEntity.getEmail().equals(emailEntity.getEmail())) {
            memberAuthEmailService.updateEmail(memberId, memberEntity.getEmail(), email);
        }
        if (usernameEntity != null && !memberEntity.getUsername().equals(usernameEntity.getUsername())){
            memberAuthUsernameService.updateUsername(memberId, memberEntity.getUsername(), username);
        }
        memberService.updateMember(memberId, vo);
    }

    @Override
    public MemberVo getInfo() {
        MemberEntity memberEntity = memberService.getByMemberId(authTokenService.getMemberInfo().getMemberId());
        MemberVo vo = new MemberVo();
        BeanUtils.copyProperties(memberEntity, vo);
        return vo;
    }

    @Override
    public void sendCode(MemberPhoneVo vo) {
        String phone = "";
        if (MemberPhoneEnum.IDENTITY.getCode() == vo.getType()) {
            phone = vo.getPhone();
        } else {
            MemberEntity memberEntity = memberService.getByMemberId(authTokenService.getMemberInfo().getMemberId());
            phone = memberEntity.getMobile();
        }
        //验证是否已发送
        Object codeCache = redisCache.getCacheObject(CacheConstants.SMS_CODE_MEMBER_KEY + phone);
        if (!Objects.isNull(codeCache)) {
            throw new PhoneCodeRepeatException();
        }
        SmsVo smsVo = new SmsVo();
        smsVo.setPhone(phone);
        //发送手机号
        thirdFeignService.sendCode(smsVo).valid();
    }

    @Override
    public void validCode(MemberCodeVo vo) {
        String phone = "";
        String memberId = authTokenService.getMemberInfo().getMemberId();
        MemberEntity memberEntity = memberService.getByMemberId(authTokenService.getMemberInfo().getMemberId());
        boolean updateMemberFlag = false;
        if (MemberPhoneEnum.IDENTITY.getCode() == vo.getType()) {
            phone = vo.getPhone();
            updateMemberFlag = true;
        } else {
            phone = memberEntity.getMobile();
        }
        Object codeCache = redisCache.getCacheObject(CacheConstants.SMS_CODE_MEMBER_KEY + phone);
        String code = vo.getCode();
        if (Objects.isNull(codeCache) || !codeCache.toString().equals(code)) {
            throw new PhoneCodeException();
        }
        if (updateMemberFlag) {
            memberEntity.setMobile(vo.getPhone());
            memberService.updatePhone(memberId, vo.getPhone());
        }

    }

    @Override
    @Transactional
    public void changePwd(MemberPwdVo vo) {
        //旧密码和新密码是否相同
        if (vo.getOldPassword().equals(vo.getNewPassword())) {
            throw new BusinessException("旧密码和新密码不能相同");
        }
        //验证新密码和确认密码是否相同
        if (!vo.getConformPassword().equals(vo.getNewPassword())) {
            throw new MemberPwdException();
        }
        //验证旧密码是否正确
        String memberId = authTokenService.getMemberInfo().getMemberId();
        MemberEntity member = memberService.getByMemberId(memberId);
        //md5验证
        if (!MD5Utils.verify(vo.getOldPassword(), member.getPassword())) {
            throw new MemberPwdException();
        }
        //验证通过，更新密码
        String salt = IdUtils.fastUUID().substring(0, 16);
        String password = MD5Utils.encrypt(vo.getNewPassword(), salt);
        memberService.updatePassword(memberId, salt, password);
    }

    /**
     * 构建用户信息
     */
    private MemberInfo constructMemberInfo(MemberEntity memberEntity) {
        MemberInfo memberInfo = new MemberInfo();
        memberInfo.setMemberId(memberEntity.getMemberId());
        memberInfo.setAccount(memberEntity.getUsername());
        memberInfo.setMobile(memberEntity.getMobile());
        memberInfo.setAvatar(memberEntity.getAvatar());
        memberInfo.setNickname(memberEntity.getNickname());
        if (memberEntity.getBirth() != null) {
            memberInfo.setBirthday(DateUtils.LocalDateTimeToString(memberEntity.getBirth()));
        }
        memberInfo.setCityCode(memberEntity.getCity());
        memberInfo.setProfession(memberEntity.getJob());
        String token = authTokenService.createToken(memberInfo);
        memberInfo.setToken(token);
        return memberInfo;
    }

    @PreventRepeatSubmit(key = "#query.account", openSpEL = true)
    public MemberEntity validateUser(LoginQuery query) {
        String account = query.getAccount();
        String password = query.getPassword();
        // 用户名或密码为空 错误
        if (StringUtils.isEmpty(account) || StringUtils.isEmpty(password)) {
            throw new MemberNotExistsException();
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < MemberConstants.PASSWORD_MIN_LENGTH
                || password.length() > MemberConstants.PASSWORD_MAX_LENGTH) {
            throw new MemberPasswordNotMatchException();
        }
        // 用户名不在指定范围内 错误
        if (account.length() < MemberConstants.USERNAME_MIN_LENGTH
                || account.length() > MemberConstants.USERNAME_MAX_LENGTH) {
            throw new MemberPasswordNotMatchException();
        }
        //登录验证
        boolean isPassAccount = false;
        String memberId = null;
        //用户名认证
        MemberAuthUsernameEntity authUsernameEntity = memberAuthUsernameService.queryByUsername(query.getAccount());
        if (authUsernameEntity != null) {
            isPassAccount = true;
            memberId = authUsernameEntity.getMemberId();
        } else {
            //邮箱认证
            MemberAuthEmailEntity authEmailEntity = memberAuthEmailService.queryByEmail(query.getAccount());
            if (authEmailEntity != null) {
                isPassAccount = true;
                memberId = authEmailEntity.getMemberId();
            }
        }
        if (!isPassAccount) {
            throw new MemberNotExistsException();
        }
        MemberEntity member = memberService.getByMemberId(memberId);
        if (member == null || !MD5Utils.verify(password, member.getPassword())) {
            throw new MemberPasswordNotMatchException();
        }
        return member;
    }

    /**
     * 这里设置事务的超时时间必须小于redis的过期时间，否则事务还没执行完成，redis已经失效，当下一个事务进来，而此时事务却执行成功，
     * 此时，redis锁已经失效，会导致用户注册两次
     */
    @Transactional(rollbackFor = Exception.class)
    @PreventRepeatSubmit(key = "#query.phone", openSpEL = true)
    public MemberEntity validatePhone(LoginQuery query) {
        String phone = query.getPhone();
        //手机格式错误
        if (!RegularValidUtils.checkPhone(phone)) {
            throw new WebDisplayException("请检查手机号是否正确");
        }
        String code = query.getCode();
        //验证码格式错误
        if (!RegularValidUtils.checkPhoneCode(code)) {
            throw new PhoneCodeException();
        }
        Object value = redisCache.getCacheObject(CacheConstants.SMS_CODE_MEMBER_KEY + query.getPhone());
        // 没有发送验证码 或者  验证码错误
        if (Objects.isNull(value) || !value.toString().equals(code)) {
            throw new PhoneCodeException();
        }
        //验证通过,若存在，则直接登录；不存在，则注册/登录
        String uuid = IdUtils.fastSimpleUUID();
        try {
            boolean flag = redisCache.setCacheIfAbsent(phone, uuid, 5L, TimeUnit.SECONDS);
            if (!flag) {
                throw new WebDisplayException("数据正在处理中，请稍后");
            }
            MemberEntity member = memberService.getByPhone(phone);
            if (member == null) {
                member = new MemberEntity();
                member.setUsername(IdUtils.getBusinessCode(SerialCodeEnum.MEMBER_USER_NO.getCode()));
                String salt = IdUtils.fastUUID().replace("-", "").substring(0, 16);
                member.setPassword(MD5Utils.encrypt("123456", salt));
                member.setSalt(salt);
                member.setMobile(phone);
                member.setNickname("还没取名哦");
                member.setLevelId(1L);
                member.setMemberId(SnowflakeIdUtils.nextSnowFlackId(phone));
                memberService.save(member);
            }
            return member;
        } finally {
            Object cacheValue = redisCache.getCacheObject(phone);
            if (!Objects.isNull(cacheValue) && uuid.equals(cacheValue)) {
                redisCache.deleteObject(phone);
            }
        }
    }
}
